Skip to content

Add support for using jj and jj worspaces#71

Draft
sundbp wants to merge 8 commits intoraine:mainfrom
sundbp:main
Draft

Add support for using jj and jj worspaces#71
sundbp wants to merge 8 commits intoraine:mainfrom
sundbp:main

Conversation

@sundbp
Copy link
Copy Markdown

@sundbp sundbp commented Mar 4, 2026

This PR adds support for jj workspaces. I attempted to start a discussion in #59 - making this draft PR as a next step in seeing if there's any interest to support this.

sundbp added 8 commits March 4, 2026 12:11
Introduce src/vcs/ module with:
- Vcs trait encapsulating all VCS operations needed by workmux
- GitVcs implementation delegating to existing git:: functions
- JjVcs stub implementation with todo errors for future phases
- detect_vcs() factory function that walks up from CWD to find .jj or .git
- VcsStatus type alias for GitStatus

This is Phase 1 of jj support: purely additive, no existing behavior changes.
The existing git:: module remains intact and all callers still use it directly.
Replace all direct git::* calls with Vcs trait calls throughout the
codebase. WorkflowContext now holds vcs: Arc<dyn Vcs> and exposes
shared_dir (renamed from git_common_dir). All workflow/ modules use
context.vcs.* methods. All command/ modules detect VCS via
vcs::detect_vcs() or vcs::try_detect_vcs(). cli.rs and config.rs
use VCS-agnostic repo discovery.

Key changes:
- DeferredCleanup stores pre-computed vcs_cleanup_commands instead
  of git_common_dir, keeping it VCS-agnostic
- workflow::list() and agent_resolve functions take &dyn Vcs param
- GitStatus type alias renamed to VcsStatus
- All worktree terminology replaced with workspace in VCS calls
- git::parse_remote_branch_spec kept as git-specific string parsing
Implement the core jj operations needed for workmux add/list/open:

Repo detection:
- get_repo_root/get_repo_root_for via .jj/ directory walking
- get_shared_dir returns repo root (where .jj/ lives)
- has_commits always true (jj has root commit)

Workspace lifecycle:
- create_workspace: jj workspace add + jj bookmark create
- list_workspaces: parse jj workspace list + metadata lookup
- find_workspace: match by handle, bookmark, or workspace name
- prune_workspaces: forget workspaces whose paths no longer exist

Metadata storage:
- set/get_workspace_meta via jj config set/get --repo
- get_all_workspace_modes by parsing .jj/repo/config.toml
- remove_workspace_meta by editing config.toml directly

Branch/bookmark operations:
- get_default_branch: probe main/master bookmarks
- branch_exists via jj bookmark list with template
- get_current_branch via jj log -r @ -T bookmarks
- list_checkout_branches, delete_branch, get_merge_base
- get_unmerged_branches via revset queries

Status operations:
- has_uncommitted_changes via jj diff --stat
- has_staged/unstaged both map to has_uncommitted (no staging area)
- has_untracked_files always false (jj auto-tracks)
- get_status with conflict detection via conflicts() revset

Also implements:
- Base branch tracking via jj config (workmux.base.<branch>)
- Deferred cleanup commands (workspace forget, bookmark delete)
- Unit tests for workspace list and diff stat parsing
Implement the merge/cleanup operations needed for workmux merge/remove:

Merge operations:
- commit_with_editor: jj commit (prompts for description)
- merge_in_workspace: jj new @ <branch> (creates merge commit)
- rebase_onto_base: jj rebase -s @ -d <base>
- merge_squash: jj squash --from <branch> --into @
- switch_branch: jj edit <bookmark>
- reset_hard: jj restore (restores working copy to parent)
- abort_merge: jj undo (undoes last operation)
- stash_push/pop remain no-ops (jj auto-commits working copy)
Implement remote operations for jj repos with git backend:

Remote operations:
- list_remotes: jj git remote list (parse name/url pairs)
- remote_exists, add_remote: jj git remote add
- set_remote_url: remove + re-add (jj has no set-url)
- get_remote_url: parse jj git remote list output
- fetch_remote: jj git fetch --remote <name>
- fetch_prune: jj git fetch (auto-prunes)
- ensure_fork_remote: construct fork URL from origin, reuses
  git-url-parse for URL manipulation
- get_repo_owner: parse owner from origin remote URL

Helper functions:
- parse_owner_from_url: extract owner from HTTPS/SSH git URLs
- construct_fork_url: build fork URL preserving host and protocol
Add comprehensive unit tests for jj VCS implementation:

Parser tests:
- Workspace list parsing (single, multiple, empty, descriptions,
  hyphenated names)
- Diff stat totals parsing (full, insertions-only, deletions-only,
  empty, single file, no changes)

URL parsing tests:
- HTTPS, SSH, HTTP formats
- GitHub Enterprise domains
- Invalid URLs, local paths

Cleanup command tests:
- Full cleanup (workspace forget + bookmark delete + config unsets)
- Keep-branch mode (no bookmark delete)
- Special characters in paths (shell quoting)

Metadata tests:
- TOML config parsing for get_all_workspace_modes
- Verifies section header matching and mode extraction

Behavioral tests:
- VCS name returns "jj"
- has_commits always returns true (jj has root commit)
- stash_push/pop are no-ops (jj auto-commits)
- has_untracked_files always false (jj auto-tracks)

26 jj-specific tests total.
Add jj workspace mentions alongside git worktree references across all
docs, skills, and README. workmux now supports both Git and jj as VCS
backends via the Vcs trait, so the documentation should reflect this.

Key changes:
- Add jj as alternative VCS in taglines, descriptions, and requirements
- Rename "Git worktree caveats" page to "Worktree caveats" with
  git-specific sections marked
- Add VCS detection step (Step 0) to merge and rebase skills
- Add jj command equivalents to merge strategies and remove --gone flag
- Broaden VCS-specific wording (e.g., "gitignored" → "ignored")
git worktree prune silently skips locked worktrees. If git worktree add
was interrupted, it leaves a "locked" file in $GIT_COMMON_DIR/worktrees/<name>/
that prevents prune from cleaning up the metadata, causing "cannot delete
branch used by worktree" errors during cleanup.

Add resolve_worktree_admin_dir() to GitVcs which reads the worktree's
.git file to find the admin directory (with fallback to the conventional
path), then:

- remove_workspace_lock(): resolves admin dir and removes the lock file
  before pruning in the synchronous cleanup path
- build_cleanup_commands(): includes an rm -f <locked> command before
  the prune command in deferred cleanup scripts

Both methods are no-ops in JjVcs since jj has no equivalent lock
mechanism.
@raine
Copy link
Copy Markdown
Owner

raine commented Mar 5, 2026

Thanks for the PR.

if there's any interest to support this

Maybe, eventually as an additional VCS backend alternative to git, with a single new documentation page covering it.

It's far too niche to be woven into every existing documentation page. This PR touches 40+ files and adds jj mentions, parenthetical alternatives, and terminology changes throughout the entire docs, README, and skill files. That level of interleaving dilutes the clarity of the existing documentation for the 99% of users who use git.

If support were to land, I'd prefer it to be contained to a single dedicated docs page (e.g., "Using workmux with jj") covering setup, differences, and caveats, with at most a brief mention and link from the main docs. The core documentation should remain git-focused. Same way as tmux is the primary multiplexer backend.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants